#include <pspsdk.h>
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspcrypt.h>
#include <psputilsforkernel.h>
#include <pspthreadman_kernel.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <pspdecrypt.h>
#include <systemctrl.h>


PSP_MODULE_INFO("pspDecrypt_Module", 0x5006, 1, 0);
PSP_MAIN_THREAD_ATTR(0);

extern int UtilsForKernel_6C6887EE(void *, u32, void *, void *);

////////// Decryption 1 //////////

// use pre-calculated keys (step1 results)

u32 g_key0[] =
{
  0x7b21f3be, 0x299c5e1d, 0x1c9c5e71, 0x96cb4645, 0x3c9b1be0, 0xeb85de3d,
  0x4a7f2022, 0xc2206eaa, 0xd50b3265, 0x55770567, 0x3c080840, 0x981d55f2,
  0x5fd8f6f3, 0xee8eb0c5, 0x944d8152, 0xf8278651, 0x2705bafa, 0x8420e533,
  0x27154ae9, 0x4819aa32, 0x59a3aa40, 0x2cb3cf65, 0xf274466d, 0x3a655605,
  0x21b0f88f, 0xc5b18d26, 0x64c19051, 0xd669c94e, 0xe87035f2, 0x9d3a5909,
  0x6f4e7102, 0xdca946ce, 0x8416881b, 0xbab097a5, 0x249125c6, 0xb34c0872,
};

u32 g_key2[] =
{
  0xccfda932, 0x51c06f76, 0x046dcccf, 0x49e1821e, 0x7d3b024c, 0x9dda5865,
  0xcc8c9825, 0xd1e97db5, 0x6874d8cb, 0x3471c987, 0x72edb3fc, 0x81c8365d,
  0xe161e33a, 0xfc92db59, 0x2009b1ec, 0xb1a94ce4, 0x2f03696b, 0x87e236d8,
  0x3b2b8ce9, 0x0305e784, 0xf9710883, 0xb039db39, 0x893bea37, 0xe74d6805,
  0x2a5c38bd, 0xb08dc813, 0x15b32375, 0x46be4525, 0x0103fd90, 0xa90e87a2,
  0x52aba66a, 0x85bf7b80, 0x45e8ce63, 0x4dd716d3, 0xf5e30d2d, 0xaf3ae456,
};

u32 g_key3[] =
{
  0xa6c8f5ca, 0x6d67c080, 0x924f4d3a, 0x047ca06a, 0x08640297, 0x4fd4a758,
  0xbd685a87, 0x9b2701c2, 0x83b62a35, 0x726b533c, 0xe522fa0c, 0xc24b06b4,
  0x459d1cac, 0xa8c5417b, 0x4fea62a2, 0x0615d742, 0x30628d09, 0xc44fab14,
  0x69ff715e, 0xd2d8837d, 0xbeed0b8b, 0x1e6e57ae, 0x61e8c402, 0xbe367a06,
  0x543f2b5e, 0xdb3ec058, 0xbe852075, 0x1e7e4dcc, 0x1564ea55, 0xec7825b4,
  0xc0538cad, 0x70f72c7f, 0x49e8c3d0, 0xeda97ec5, 0xf492b0a4, 0xe05eb02a,
};

u32 g_key44[] =
{
  0xef80e005, 0x3a54689f, 0x43c99ccd, 0x1b7727be, 0x5cb80038, 0xdd2efe62,
  0xf369f92c, 0x160f94c5, 0x29560019, 0xbf3c10c5, 0xf2ce5566, 0xcea2c626,
  0xb601816f, 0x64e7481e, 0x0c34debd, 0x98f29cb0, 0x3fc504d7, 0xc8fb39f0,
  0x0221b3d8, 0x63f936a2, 0x9a3a4800, 0x6ecc32e3, 0x8e120cfd, 0xb0361623,
  0xaee1e689, 0x745502eb, 0xe4a6c61c, 0x74f23eb4, 0xd7fa5813, 0xb01916eb,
  0x12328457, 0xd2bc97d2, 0x646425d8, 0x328380a5, 0x43da8ab1, 0x4b122ac9,
};

u32 g_key20[] =
{
  0x33b50800, 0xf32f5fcd, 0x3c14881f, 0x6e8a2a95, 0x29feefd5, 0x1394eae3,
  0xbd6bd443, 0x0821c083, 0xfab379d3, 0xe613e165, 0xf5a754d3, 0x108b2952,
  0x0a4b1e15, 0x61eadeba, 0x557565df, 0x3b465301, 0xae54ecc3, 0x61423309,
  0x70c9ff19, 0x5b0ae5ec, 0x989df126, 0x9d987a5f, 0x55bc750e, 0xc66eba27,
  0x2de988e8, 0xf76600da, 0x0382dccb, 0x5569f5f2, 0x8e431262, 0x288fe3d3,
  0x656f2187, 0x37d12e9c, 0x2f539eb4, 0xa492998e, 0xed3958f7, 0x39e96523,
};

u32 g_key3A[] =
{
  0x67877069, 0x3abd5617, 0xc23ab1dc, 0xab57507d, 0x066a7f40, 0x24def9b9,
  0x06f759e4, 0xdcf524b1, 0x13793e5e, 0x0359022d, 0xaae7e1a2, 0x76b9b2fa,
  0x9a160340, 0x87822fba, 0x19e28fbb, 0x9e338a02, 0xd8007e9a, 0xea317af1,
  0x630671de, 0x0b67ca7c, 0x865192af, 0xea3c3526, 0x2b448c8e, 0x8b599254,
  0x4602e9cb, 0x4de16cda, 0xe164d5bb, 0x07ecd88e, 0x99ffe5f8, 0x768800c1,
  0x53b091ed, 0x84047434, 0xb426dbbc, 0x36f948bb, 0x46142158, 0x749bb492,
};

/* updaters keys */
u8 updaters_keys[0x90+0x14] = 
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x0B, 0x01, 0x1C, 0xE7, 0x31, 0x15, 0x6B, 0x83, 
	0x3E, 0x26, 0x0D, 0xCC, 0x69, 0x36, 0x12, 0xCB, 
	0xA7, 0xFD, 0x26, 0x66, 0x93, 0x2A, 0x6E, 0x1A, 
	0x91, 0x2E, 0xC6, 0xFC, 0xD8, 0x2F, 0x00, 0x13, 
	0x5A, 0xE2, 0xDF, 0xB6, 0xA2, 0xE4, 0x27, 0xC8, 
	0x18, 0xC3, 0x50, 0x50, 0xB7, 0xE9, 0x4A, 0xED, 
	0xCC, 0x3C, 0x30, 0xFD, 0x10, 0x6A, 0x2B, 0x0A, 
	0x22, 0xCB, 0xC6, 0xE0, 0x20, 0x65, 0x12, 0xEB, 
	0x7D, 0x4E, 0x2A, 0x37, 0x0B, 0x0A, 0xEF, 0x88, 
	0xDA, 0x06, 0x54, 0xD4, 0x30, 0xAF, 0xCD, 0xCA, 
	0x9A, 0xF9, 0xDA, 0x1A, 0xB0, 0x1B, 0xBB, 0x62, 
	0x0C, 0xDB, 0xF8, 0x44, 0x73, 0x56, 0x14, 0x8E, 
	0x93, 0xB1, 0x2C, 0xFD, 0x67, 0xE2, 0x5D, 0xCB, 
	0x48, 0x5B, 0xD9, 0xB3, 0x54, 0x14, 0xD7, 0x9F, 
	0x79, 0x9C, 0x24, 0xE9, 0xC2, 0x7A, 0x4E, 0x8C, 
	0x4D, 0x24, 0x19, 0x94, 0xFF, 0xC9, 0xC2, 0x2D, 
	0x23, 0x63, 0x51, 0xB8, 0xFA, 0xD6, 0x7F, 0xE6, 
	0x5E, 0xBC, 0x32, 0xB2, 0x02, 0x13, 0xC4, 0x76
};

/* locoroco, kazue, and maybe others demos keys */
u8 demo_keys0[0x90+0x14] = 
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00,
	0x82, 0x4C, 0xA5, 0x18, 0xD3, 0xC8, 0x6E, 0xEA, 
	0x17, 0x41, 0x04, 0xDC, 0xEA, 0xC5, 0x01, 0xFC, 
	0x97, 0xB1, 0x94, 0x54, 0x71, 0x19, 0x22, 0xEE, 
	0xE0, 0x2D, 0xE9, 0x83, 0x3D, 0x64, 0x30, 0xE6, 
	0x42, 0x5C, 0x30, 0x5F, 0xEB, 0x41, 0xA0, 0xE0, 
	0x62, 0xC6, 0x63, 0xEE, 0x5D, 0xA5, 0x0D, 0x1E, 
	0xC2, 0x10, 0x14, 0x49, 0x06, 0xC6, 0x93, 0x84, 
	0x71, 0xA5, 0x42, 0x63, 0x13, 0xF0, 0xB6, 0xD5, 
	0x43, 0x51, 0x9E, 0xFA, 0x91, 0x0A, 0x7C, 0xE1, 
	0x58, 0x1B, 0x95, 0x25, 0x40, 0x11, 0xF1, 0x8D, 
	0xB1, 0x01, 0x8D, 0x04, 0x09, 0x54, 0x5C, 0x54, 
	0xF5, 0x53, 0x08, 0xB0, 0x53, 0x85, 0xB4, 0xCE, 
	0x0B, 0xF5, 0xC3, 0xFB, 0xC6, 0x55, 0x24, 0x0B, 
	0xF2, 0xC6, 0x2C, 0xE4, 0x0C, 0xF0, 0x05, 0x3C, 
	0xD7, 0x6C, 0x39, 0xD5, 0x87, 0x22, 0x09, 0xF7, 
	0x3D, 0xC5, 0xA2, 0xFD, 0x55, 0x92, 0x3F, 0xB1, 
	0xF6, 0xFE, 0xC8, 0x18, 0x1D, 0x6B, 0x04, 0x52, 
	0x5F, 0x8C, 0xE8, 0xE7, 0x26, 0x5A, 0x6E, 0x5A
};

typedef struct
{
    u32 tag; // 4 byte value at offset 0xD0 in the PRX file
    u8* key; // "step1_result" use for XOR step
    u8 code;
    u8 codeExtra;
} TAG_INFO;

static const TAG_INFO g_tagInfo[] =
{
    // 1.x PRXs
    { 0x00000000, (u8*)g_key0, 0x42 },
    { 0x02000000, (u8*)g_key2, 0x45 },
    { 0x03000000, (u8*)g_key3, 0x46 },

    // 2.0 PRXs
    { 0x4467415d, (u8*)g_key44, 0x59, 0x59 },
    { 0x207bbf2f, (u8*)g_key20, 0x5A, 0x5A },
    { 0x3ace4dce, (u8*)g_key3A, 0x5B, 0x5B },

	 // updaters
    { 0x0b000000, updaters_keys, 0x4E },
	
	// locoroco, kazue, demos	
	{ 0x0c000000, demo_keys0, 0x4F },
};

static TAG_INFO const* GetTagInfo(u32 tagFind)
{
    int iTag;
    for (iTag = 0; iTag < sizeof(g_tagInfo)/sizeof(TAG_INFO); iTag++)
        if (g_tagInfo[iTag].tag == tagFind)
            return &g_tagInfo[iTag];
    return NULL; // not found
}

static void ExtraV2Mangle(u8* buffer1, u8 codeExtra)
{
    static u8 g_dataTmp[20+0xA0] __attribute__((aligned(0x40)));
    u8* buffer2 = g_dataTmp; // aligned

    memcpy(buffer2+20, buffer1, 0xA0);
    u32* pl2 = (u32*)buffer2;
    pl2[0] = 5;
    pl2[1] = pl2[2] = 0;
    pl2[3] = codeExtra;
    pl2[4] = 0xA0;

    int ret = sceUtilsBufferCopyWithRange(buffer2, 20+0xA0, buffer2, 20+0xA0, 7);
    if (ret != 0)
        Kprintf("extra de-mangle returns %d\n", ret);
    // copy result back
    memcpy(buffer1, buffer2, 0xA0);
}

static int Scramble(u32 *buf, u32 size, u32 code)
{
	buf[0] = 5;
	buf[1] = buf[2] = 0;
	buf[3] = code;
	buf[4] = size;

	if (sceUtilsBufferCopyWithRange(buf, size+0x14, buf, size+0x14, 7) < 0)
	{
		return -1;
	}

	return 0;
}

static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag)
{
	int i, retsize;
	u8 bD0[0x80], b80[0x50], b00[0x80], bB0[0x20];
	
	TAG_INFO const* pti = GetTagInfo(tag);
    if (pti == NULL)
        return -1;

	retsize = *(u32*)&pbIn[0xB0];

	for (i = 0; i < 0x14; i++)
	{
		if (pti->key[i] != 0)
			break;
	}

	if (i == 0x14)
	{
		Scramble((u32 *)pti->key, 0x90, pti->code);
	}

    // build conversion into pbOut

	if (pbIn != pbOut)
		memcpy(pbOut, pbIn, cbTotal);

	memcpy(bD0, pbIn+0xD0, 0x80);
	memcpy(b80, pbIn+0x80, 0x50);
	memcpy(b00, pbIn+0x00, 0x80);
	memcpy(bB0, pbIn+0xB0, 0x20);
    
	memset(pbOut, 0, 0x150);
    memset(pbOut, 0x55, 0x40); // first $40 bytes ignored

    // step3 demangle in place
    u32* pl = (u32*)(pbOut+0x2C);
    pl[0] = 5; // number of ulongs in the header
    pl[1] = pl[2] = 0;
    pl[3] = pti->code; // initial seed for PRX
    pl[4] = 0x70;   // size

    // redo part of the SIG check (step2)
    u8 buffer1[0x150];
    memcpy(buffer1+0x00, bD0, 0x80);
    memcpy(buffer1+0x80, b80, 0x50);
    memcpy(buffer1+0xD0, b00, 0x80);
    if (pti->codeExtra != 0)
        ExtraV2Mangle(buffer1+0x10, pti->codeExtra);
    memcpy(pbOut+0x40 /* 0x2C+20 */, buffer1+0x40, 0x40);

    int ret;
    int iXOR;
    for (iXOR = 0; iXOR < 0x70; iXOR++)
        pbOut[0x40+iXOR] = pbOut[0x40+iXOR] ^ pti->key[0x14+iXOR];

    ret = sceUtilsBufferCopyWithRange(pbOut+0x2C, 20+0x70, pbOut+0x2C, 20+0x70, 7);
    if (ret != 0)
    {
        Kprintf("mangle#7 returned $%x\n", ret);
        return -1;
    }

    for (iXOR = 0x6F; iXOR >= 0; iXOR--)
        pbOut[0x40+iXOR] = pbOut[0x2C+iXOR] ^ pti->key[0x20+iXOR];

    memset(pbOut+0x80, 0, 0x30); // $40 bytes kept, clean up
    pbOut[0xA0] = 1;
    // copy unscrambled parts from header
    memcpy(pbOut+0xB0, bB0, 0x20); // file size + lots of zeros
    memcpy(pbOut+0xD0, b00, 0x80); // ~PSP header

    // step4: do the actual decryption of code block
    //  point 0x40 bytes into the buffer to key info
    ret = sceUtilsBufferCopyWithRange(pbOut, cbTotal, pbOut+0x40, cbTotal-0x40, 0x1);
    if (ret != 0)
    {
        Kprintf("mangle#1 returned $%x\n", ret);
        return -1;
    }

    // return cbTotal - 0x150; // rounded up size
	return retsize; 
}

////////// Decryption 2 //////////

/* kernel modules 2.60-2.71 */
u8 keys260_0[0x10] =
{
	0xC3, 0x24, 0x89, 0xD3, 0x80, 0x87, 0xB2, 0x4E,
	0x4C, 0xD7, 0x49, 0xE4, 0x9D, 0x1D, 0x34, 0xD1

};

/* user modules 2.60-2.71 */
u8 keys260_1[0x10] =
{
	0xF3, 0xAC, 0x6E, 0x7C, 0x04, 0x0A, 0x23, 0xE7,
	0x0D, 0x33, 0xD8, 0x24, 0x73, 0x39, 0x2B, 0x4A
};

/* vshmain 2.60-2.71 */
u8 keys260_2[0x10] =
{
	0x72, 0xB4, 0x39, 0xFF, 0x34, 0x9B, 0xAE, 0x82,
	0x30, 0x34, 0x4A, 0x1D, 0xA2, 0xD8, 0xB4, 0x3C
};

/* kernel modules 2.80 */
u8 keys280_0[0x10] =
{
	0xCA, 0xFB, 0xBF, 0xC7, 0x50, 0xEA, 0xB4, 0x40,
	0x8E, 0x44, 0x5C, 0x63, 0x53, 0xCE, 0x80, 0xB1
};

/* user modules 2.80 */
u8 keys280_1[0x10] =
{
	0x40, 0x9B, 0xC6, 0x9B, 0xA9, 0xFB, 0x84, 0x7F,
	0x72, 0x21, 0xD2, 0x36, 0x96, 0x55, 0x09, 0x74
};

/* vshmain executable 2.80 */
u8 keys280_2[0x10] =
{
	0x03, 0xA7, 0xCC, 0x4A, 0x5B, 0x91, 0xC2, 0x07,
	0xFF, 0xFC, 0x26, 0x25, 0x1E, 0x42, 0x4B, 0xB5
};

/* kernel modules 3.00 */
u8 keys300_0[0x10] =
{
	0x9F, 0x67, 0x1A, 0x7A, 0x22, 0xF3, 0x59, 0x0B,
    0xAA, 0x6D, 0xA4, 0xC6, 0x8B, 0xD0, 0x03, 0x77

};

/* user modules 3.00 */
u8 keys300_1[0x10] =
{
	0x15, 0x07, 0x63, 0x26, 0xDB, 0xE2, 0x69, 0x34,
    0x56, 0x08, 0x2A, 0x93, 0x4E, 0x4B, 0x8A, 0xB2

};

/* vshmain 3.00 */
u8 keys300_2[0x10] =
{
	0x56, 0x3B, 0x69, 0xF7, 0x29, 0x88, 0x2F, 0x4C,
    0xDB, 0xD5, 0xDE, 0x80, 0xC6, 0x5C, 0xC8, 0x73

};

/* kernel modules 3.00 */
u8 keys303_0[0x10] =
{
	0x7b, 0xa1, 0xe2, 0x5a, 0x91, 0xb9, 0xd3, 0x13,
	0x77, 0x65, 0x4a, 0xb7, 0xc2, 0x8a, 0x10, 0xaf
};

/* kernel modules 3.10 */
u8 keys310_0[0x10] =
{
	0xa2, 0x41, 0xe8, 0x39, 0x66, 0x5b, 0xfa, 0xbb,
	0x1b, 0x2d, 0x6e, 0x0e, 0x33, 0xe5, 0xd7, 0x3f
};

/* user modules 3.10 */
u8 keys310_1[0x10] =
{
	0xA4, 0x60, 0x8F, 0xAB, 0xAB, 0xDE, 0xA5, 0x65,
	0x5D, 0x43, 0x3A, 0xD1, 0x5E, 0xC3, 0xFF, 0xEA
};

/* vshmain 3.10 */
u8 keys310_2[0x10] =
{
	0xE7, 0x5C, 0x85, 0x7A, 0x59, 0xB4, 0xE3, 0x1D,
	0xD0, 0x9E, 0xCE, 0xC2, 0xD6, 0xD4, 0xBD, 0x2B
};

/* reboot.bin 3.10 */
u8 keys310_3[0x10] =
{
    0x2E, 0x00, 0xF6, 0xF7, 0x52, 0xCF, 0x95, 0x5A,
    0xA1, 0x26, 0xB4, 0x84, 0x9B, 0x58, 0x76, 0x2F
};

/* kernel modules 3.30 */ 
u8 keys330_0[0x10] = 
{ 
	0x3B, 0x9B, 0x1A, 0x56, 0x21, 0x80, 0x14, 0xED,
	0x8E, 0x8B, 0x08, 0x42, 0xFA, 0x2C, 0xDC, 0x3A
};

/* user modules 3.30 */ 
u8 keys330_1[0x10] = 
{ 
    0xE8, 0xBE, 0x2F, 0x06, 0xB1, 0x05, 0x2A, 0xB9, 
    0x18, 0x18, 0x03, 0xE3, 0xEB, 0x64, 0x7D, 0x26 
}; 

/* vshmain 3.30 */ 
u8 keys330_2[0x10] = 
{ 
    0xAB, 0x82, 0x25, 0xD7, 0x43, 0x6F, 0x6C, 0xC1, 
    0x95, 0xC5, 0xF7, 0xF0, 0x63, 0x73, 0x3F, 0xE7 
}; 

/* reboot.bin 3.30 */ 
u8 keys330_3[0x10] = 
{ 
    0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F, 
    0x38, 0x4C, 0x4D, 0x96, 0xBD, 0x49, 0xEC, 0x9B 
}; 

/* stdio.prx 3.30 */
u8 keys330_4[0x10] =
{
	0xEC, 0x3B, 0xD2, 0xC0, 0xFA, 0xC1, 0xEE, 0xB9,
	0x9A, 0xBC, 0xFF, 0xA3, 0x89, 0xF2, 0x60, 0x1F
};

/* 3.60 common kernel modules */
u8 keys360_0[16] = 
{
	0x3C, 0x2B, 0x51, 0xD4, 0x2D, 0x85, 0x47, 0xDA, 
	0x2D, 0xCA, 0x18, 0xDF, 0xFE, 0x54, 0x09, 0xED
};

/* 3.60 specific slim kernel modules */
u8 keys360_1[16] = 
{
	0x31, 0x1F, 0x98, 0xD5, 0x7B, 0x58, 0x95, 0x45, 
	0x32, 0xAB, 0x3A, 0xE3, 0x89, 0x32, 0x4B, 0x34
};

/* 3.70 common and fat kernel modules */
u8 keys370_0[0x10] = 
{
	0x26, 0x38, 0x0A, 0xAC, 0xA5, 0xD8, 0x74, 0xD1, 
	0x32, 0xB7, 0x2A, 0xBF, 0x79, 0x9E, 0x6D, 0xDB
};

/* 3.70 slim specific kernel modules */
u8 keys370_1[0x10] = 
{
	0x53, 0xE7, 0xAB, 0xB9, 0xC6, 0x4A, 0x4B, 0x77, 
	0x92, 0x17, 0xB5, 0x74, 0x0A, 0xDA, 0xA9, 0xEA
};

/* some 3.70 slim user modules */
u8 keys370_2[16] = 
{
	0x71, 0x10, 0xF0, 0xA4, 0x16, 0x14, 0xD5, 0x93, 
	0x12, 0xFF, 0x74, 0x96, 0xDF, 0x1F, 0xDA, 0x89
};

/* 3.90 kernel */
u8 keys390_0[16] = 
{
	0x45, 0xEF, 0x5C, 0x5D, 0xED, 0x81, 0x99, 0x84, 
	0x12, 0x94, 0x8F, 0xAB, 0xE8, 0x05, 0x6D, 0x7D
};

/* 3.90 slim */
u8 keys390_1[16] = 
{
	0x70, 0x1B, 0x08, 0x25, 0x22, 0xA1, 0x4D, 0x3B, 
	0x69, 0x21, 0xF9, 0x71, 0x0A, 0xA8, 0x41, 0xA9
};

/* 5.00 kernel */
u8 keys500_0[16] = 
{
	0xEB, 0x1B, 0x53, 0x0B, 0x62, 0x49, 0x32, 0x58, 
	0x1F, 0x83, 0x0A, 0xF4, 0x99, 0x3D, 0x75, 0xD0
};

/* 5.00 kernel 2000 specific */
u8 keys500_1[16] = 
{
	0xBA, 0xE2, 0xA3, 0x12, 0x07, 0xFF, 0x04, 0x1B, 
	0x64, 0xA5, 0x11, 0x85, 0xF7, 0x2F, 0x99, 0x5B
};

/* 5.00 kernel 3000 specific */
u8 keys500_2[16] = 
{
	0x2C, 0x8E, 0xAF, 0x1D, 0xFF, 0x79, 0x73, 0x1A, 
 	0xAD, 0x96, 0xAB, 0x09, 0xEA, 0x35, 0x59, 0x8B
};

u8 keys500_c[16] = 
{
	0xA3, 0x5D, 0x51, 0xE6, 0x56, 0xC8, 0x01, 0xCA,
	0xE3, 0x77, 0xBF, 0xCD, 0xFF, 0x24, 0xDA, 0x4D
};

u8 keys505_a[16] =
{
	0x7B, 0x94, 0x72, 0x27, 0x4C, 0xCC, 0x54, 0x3B,
	0xAE, 0xDF, 0x46, 0x37, 0xAC, 0x01, 0x4D, 0x87
};

u8 keys505_0[16] =
{
	0x2E, 0x8E, 0x97, 0xA2, 0x85, 0x42, 0x70, 0x73,
	0x18, 0xDA, 0xA0, 0x8A, 0xF8, 0x62, 0xA2, 0xB0
};

u8 keys505_1[16] =
{
	0x58, 0x2A, 0x4C, 0x69, 0x19, 0x7B, 0x83, 0x3D,
	0xD2, 0x61, 0x61, 0xFE, 0x14, 0xEE, 0xAA, 0x11
};

/* for psp 2000 file table and ipl pre-decryption */
u8 keys02G_E[0x10] = 
{
	0x9D, 0x09, 0xFD, 0x20, 0xF3, 0x8F, 0x10, 0x69, 
	0x0D, 0xB2, 0x6F, 0x00, 0xCC, 0xC5, 0x51, 0x2E
};

/* for psp 3000 file table and ipl pre-decryption */
u8 keys03G_E[0x10] = 
{
	0x4F, 0x44, 0x5C, 0x62, 0xB3, 0x53, 0xC4, 0x30, 
	0xFC, 0x3A, 0xA4, 0x5B, 0xEC, 0xFE, 0x51, 0xEA
};

/* for psp go file table and ipl pre-decryption */
u8 keys05G_E[0x10] = 
{
	0x5D, 0xAA, 0x72, 0xF2, 0x26, 0x60, 0x4D, 0x1C,
	0xE7, 0x2D, 0xC8, 0xA3, 0x2F, 0x79, 0xC5, 0x54
};

/* 5.70 PSPgo kernel*/
u8 keys570_5k[0x10] =
{
	0x6D, 0x72, 0xA4, 0xBA, 0x7F, 0xBF, 0xD1, 0xF1,
	0xA9, 0xF3, 0xBB, 0x07, 0x1B, 0xC0, 0xB3, 0x66
};

/* 6.00-6.20 kernel and phat */
u8 keys620_0[0x10] = 
{
	0xD6, 0xBD, 0xCE, 0x1E, 0x12, 0xAF, 0x9A, 0xE6,
	0x69, 0x30, 0xDE, 0xDA, 0x88, 0xB8, 0xFF, 0xFB
};

/* 6.00-6.20 slim kernel */
u8 keys620_1[0x10] = 
{
	0x1D, 0x13, 0xE9, 0x50, 0x04, 0x73, 0x3D, 0xD2,
	0xE1, 0xDA, 0xB9, 0xC1, 0xE6, 0x7B, 0x25, 0xA7
};

u8 keys620_a[0x10] = 
{
	0xAC, 0x34, 0xBA, 0xB1, 0x97, 0x8D, 0xAE, 0x6F,
	0xBA, 0xE8, 0xB1, 0xD6, 0xDF, 0xDF, 0xF1, 0xA2
};

u8 keys620_e[0x10] = 
{
	0xB1, 0xB3, 0x7F, 0x76, 0xC3, 0xFB, 0x88, 0xE6,
	0xF8, 0x60, 0xD3, 0x35, 0x3C, 0xA3, 0x4E, 0xF3
};

/* PSPgo internal */
u8 keys620_5[0x10] =
{
	0xF1, 0xBC, 0x17, 0x07, 0xAE, 0xB7, 0xC8, 0x30,
	0xD8, 0x34, 0x9D, 0x40, 0x6A, 0x8E, 0xDF, 0x4E
};

/* 6.XX PSPgo kernel */
u8 keys620_5k[0x10] =
{
	0x41, 0x8A, 0x35, 0x4F, 0x69, 0x3A, 0xDF, 0x04,
	0xFD, 0x39, 0x46, 0xA2, 0x5C, 0x2D, 0xF2, 0x21
};

u8 keys620_5v[0x10] =
{
	0xF2, 0x8F, 0x75, 0xA7, 0x31, 0x91, 0xCE, 0x9E,
	0x75, 0xBD, 0x27, 0x26, 0xB4, 0xB4, 0x0C, 0x32
};

/* 6.30 phat kernel */ 
u8 keys630_k1[0x10] = {
	0x36, 0xB0, 0xDC, 0xFC, 0x59, 0x2A, 0x95, 0x1D, 
	0x80, 0x2D, 0x80, 0x3F, 0xCD, 0x30, 0xA0, 0x1B,
};

/* 6.30 phat kernel-2 */
u8 keys630_k2[0x10] = {
	0xd4, 0x35, 0x18, 0x02, 0x29, 0x68, 0xfb, 0xa0, 
	0x6a, 0xa9, 0xa5, 0xed, 0x78, 0xfd, 0x2e, 0x9d
};

u8 key_380280f0[0x10] =
{
	0x97, 0x09, 0x12, 0xD3, 0xDB, 0x02, 0xBD, 0xD8, 
	0xE7, 0x74, 0x51, 0xFE, 0xF0, 0xEA, 0x6C, 0x5C,
};

/* 6.30 slim kernel */
u8 keys630_k3[0x10] =
{
	0x23, 0x8D, 0x3D, 0xAE, 0x41, 0x50, 0xA0, 0xFA,
	0xF3, 0x2F, 0x32, 0xCE, 0xC7, 0x27, 0xCD, 0x50,
};

/* 6.30 slim pops */
u8 keys630_k4[0x10] = 
{
	0xAA, 0xA1, 0xB5, 0x7C, 0x93, 0x5A, 0x95, 0xBD,
	0xEF, 0x69, 0x16, 0xFC, 0x2B, 0x92, 0x31, 0xDD
};

u8 keys630_k5[0x10] = { 0x87,0x37,0x21,0xCC,0x65,0xAE,0xAA,0x5F,0x40,0xF6,0x6F,0x2A,0x86,0xC7,0xA1,0xC8 };
u8 keys630_k6[0x10] = { 0x8D,0xDB,0xDC,0x5C,0xF2,0x70,0x2B,0x40,0xB2,0x3D,0x00,0x09,0x61,0x7C,0x10,0x60 };
u8 keys630_k7[0x10] = { 0x77,0x1C,0x06,0x5F,0x53,0xEC,0x3F,0xFC,0x22,0xCE,0x5A,0x27,0xFF,0x78,0xA8,0x48 };
u8 keys630_k8[0x10] = { 0x81,0xD1,0x12,0x89,0x35,0xC8,0xEA,0x8B,0xE0,0x02,0x2D,0x2D,0x6A,0x18,0x67,0xB8 };

u8 keys636_k1[0x10] = { 0x07,0xE3,0x08,0x64,0x7F,0x60,0xA3,0x36,0x6A,0x76,0x21,0x44,0xC9,0xD7,0x06,0x83 };
u8 keys636_k2[0x10] = { 0x91,0xF2,0x02,0x9E,0x63,0x32,0x30,0xA9,0x1D,0xDA,0x0B,0xA8,0xB7,0x41,0xA3,0xCC };

u8 keys639_k3[0x10] = { 0x01, 0x7B, 0xF0, 0xE9, 0xBE, 0x9A, 0xDD, 0x54, 0x37, 0xEA, 0x0E, 0xC4, 0xD6, 0x4D, 0x8E, 0x9E };

u8 keys638_k4[0x10] = { 0x98, 0x43, 0xFF, 0x85, 0x68, 0xB2, 0xDB, 0x3B, 0xD4, 0x22, 0xD0, 0x4F, 0xAB, 0x5F, 0x0A, 0x31 };

u8 keys600_1[0x10] = { 0xE3,0x52,0x39,0x97,0x3B,0x84,0x41,0x1C,0xC3,0x23,0xF1,0xB8,0xA9,0x09,0x4B,0xF0 };
u8 keys600_2[0x10] = { 0xE1,0x45,0x93,0x2C,0x53,0xE2,0xAB,0x06,0x6F,0xB6,0x8F,0x0B,0x66,0x91,0xE7,0x1E };

u8 key_380283F0[0x10] = { 0x34,0x20,0x0C,0x8E,0xA1,0x86,0x79,0x84,0xAF,0x13,0xAE,0x34,0x77,0x6F,0xEA,0x89 };

u8 keys660_k1[0x10] = { 0x76, 0xF2, 0x6C, 0x0A, 0xCA, 0x3A, 0xBA, 0x4E, 0xAC, 0x76, 0xD2, 0x40, 0xF5, 0xC3, 0xBF, 0xF9 };
u8 keys660_k2[0x10] = { 0x7A, 0x3E, 0x55, 0x75, 0xB9, 0x6A, 0xFC, 0x4F, 0x3E, 0xE3, 0xDF, 0xB3, 0x6C, 0xE8, 0x2A, 0x82 };
u8 keys660_k3[0x10] = { 0xFA, 0x79, 0x09, 0x36, 0xE6, 0x19, 0xE8, 0xA4, 0xA9, 0x41, 0x37, 0x18, 0x81, 0x02, 0xE9, 0xB3 };
u8 keys660_v1[0x10] = { 0xBA, 0x76, 0x61, 0x47, 0x8B, 0x55, 0xA8, 0x72, 0x89, 0x15, 0x79, 0x6D, 0xD7, 0x2F, 0x78, 0x0E };
u8 keys660_v2[0x10] = { 0xF9, 0x4A, 0x6B, 0x96, 0x79, 0x3F, 0xEE, 0x0A, 0x04, 0xC8, 0x8D, 0x7E, 0x5F, 0x38, 0x3A, 0xCF };
u8 keys660_v3[0x10] = { 0x88, 0xAF, 0x18, 0xE9, 0xC3, 0xAA, 0x6B, 0x56, 0xF7, 0xC5, 0xA8, 0xBF, 0x1A, 0x84, 0xE9, 0xF3 };
u8 keys660_v4[0x10] = { 0xD1, 0xB0, 0xAE, 0xC3, 0x24, 0x36, 0x13, 0x49, 0xD6, 0x49, 0xD7, 0x88, 0xEA, 0xA4, 0x99, 0x86 };
u8 keys660_v5[0x10] = { 0xCB, 0x93, 0x12, 0x38, 0x31, 0xC0, 0x2D, 0x2E, 0x7A, 0x18, 0x5C, 0xAC, 0x92, 0x93, 0xAB, 0x32 };
u8 keys660_v6[0x10] = { 0x92, 0x8C, 0xA4, 0x12, 0xD6, 0x5C, 0x55, 0x31, 0x5B, 0x94, 0x23, 0x9B, 0x62, 0xB3, 0xDB, 0x47 };
u8 keys660_k4[0x10] = { 0xC8, 0xA0, 0x70, 0x98, 0xAE, 0xE6, 0x2B, 0x80, 0xD7, 0x91, 0xE6, 0xCA, 0x4C, 0xA9, 0x78, 0x4E };
u8 keys660_k5[0x10] = { 0xBF, 0xF8, 0x34, 0x02, 0x84, 0x47, 0xBD, 0x87, 0x1C, 0x52, 0x03, 0x23, 0x79, 0xBB, 0x59, 0x81 };
u8 keys660_k6[0x10] = { 0xD2, 0x83, 0xCC, 0x63, 0xBB, 0x10, 0x15, 0xE7, 0x7B, 0xC0, 0x6D, 0xEE, 0x34, 0x9E, 0x4A, 0xFA };	
u8 keys660_k7[0x10] = { 0xEB, 0xD9, 0x1E, 0x05, 0x3C, 0xAE, 0xAB, 0x62, 0xE3, 0xB7, 0x1F, 0x37, 0xE5, 0xCD, 0x68, 0xC3 };	
u8 keys660_v7[0x10] = { 0xC5, 0x9C, 0x77, 0x9C, 0x41, 0x01, 0xE4, 0x85, 0x79, 0xC8, 0x71, 0x63, 0xA5, 0x7D, 0x4F, 0xFB };
u8 keys660_v8[0x10] = { 0x86, 0xA0, 0x7D, 0x4D, 0xB3, 0x6B, 0xA2, 0xFD, 0xF4, 0x15, 0x85, 0x70, 0x2D, 0x6A, 0x0D, 0x3A };
u8 keys660_k8[0x10] = { 0x85, 0x93, 0x1F, 0xED, 0x2C, 0x4D, 0xA4, 0x53, 0x59, 0x9C, 0x3F, 0x16, 0xF3, 0x50, 0xDE, 0x46 };
typedef struct
{
    u32 tag; // 4 byte value at offset 0xD0 in the PRX file
    u8  *key; // 16 bytes keys
    u8 code; // code for scramble
	u8 type;
} TAG_INFO2;

static TAG_INFO2 g_tagInfo2[] =
{
	{ 0x4C9494F0, keys660_k1, 0x43}, // 6.60 
	{ 0x4C9495F0, keys660_k2, 0x43}, // 6.60 02g
	{ 0x4C9490F0, keys660_k3, 0x43}, // 6.60 
	{ 0x4C9491F0, keys660_k8, 0x43}, // 6.60 02g
	{ 0x4C9493F0, keys660_k4, 0x43}, // 6.60 05g
	{ 0x4C9497F0, keys660_k5, 0x43}, // 6.60 05g
	{ 0x4C9492F0, keys660_k6, 0x43}, // 6.60 03g 04g
	{ 0x4C9496F0, keys660_k7, 0x43}, // 6.60 03g 04g
	{ 0x4C948AF0, keys636_k1, 0x43, 3}, // 6.36
	{ 0x4C948BF0, keys636_k2, 0x43, 3}, // 6.36 02g
	{ 0x4C948CF0, keys639_k3, 0x43, 3}, // 6.36 03g 04g
	{ 0x4C948DF0, keys638_k4, 0x43, 3}, // 6.38 05g

	{ 0x457B90F0, keys660_v1, 0x5b}, // 6.60 
	{ 0x457B91F0, keys660_v7, 0x5b}, // 6.60 02g
	{ 0x457B92F0, keys660_v6, 0x5b}, // 6.60 03g 04g
	{ 0x457B93F0, keys660_v3, 0x5b}, // 6.60 05g
	
	{ 0x457b80f0, keys630_k2, 0x5B, 3}, // 6.30
	{ 0x457B81F0, keys630_k4, 0x5B, 3}, // 6.30 02g
	{ 0x457B82F0, keys630_k5, 0x5B, 3}, // 6.30 03g 04g 07g 09g
	{ 0x457B83F0, keys630_k7, 0x5B, 3}, // 6.30 05g
	{ 0x4C9484F0, keys630_k1, 0x43, 3}, // 6.30
	{ 0x4C9485F0, keys630_k3, 0x43, 3}, // 6.30 02g
	{ 0x4C9486F0, keys630_k6, 0x43, 3}, // 6.30 03g 04g 07g 09g
	{ 0x4C9487F0, keys630_k8, 0x43, 3}, // 6.30 05g

	{ 0x380290F0, keys660_v2, 0x5A }, // 6.60 vshmain
	{ 0x380291F0, keys660_v8, 0x5A }, // 6.60 vshmain 02g
	{ 0x380292F0, keys660_v4, 0x5A }, // 6.60 vshmain 03g 04g
	{ 0x380293F0, keys660_v5, 0x5A }, // 6.60 vshmain 05g
	
	{ 0x380283F0, key_380283F0, 0x5A, 3}, // 6.30 vshmain 05g
	{ 0x380280f0, key_380280f0, 0x5A, 3}, // 6.30 vshmain
	
	{ 0x457B28F0, keys620_e, 0x5B },
	{ 0x457B0CF0, keys620_a, 0x5B },
	{ 0x380228F0, keys620_5v, 0x5A }, // PSPgo 6.XX vshmain
	{ 0x4C942AF0, keys620_5k, 0x43 }, // PSPgo 6.XX
	{ 0x4C9428F0, keys620_5 , 0x43 }, // PSPgo
	{ 0x4C9422F0, keys600_2, 0x43 }, // 6.00 03g 04g
	{ 0x4C941EF0, keys600_1, 0x43 },
	{ 0x4C941DF0, keys620_1, 0x43 },
	{ 0x4C941CF0, keys620_0, 0x43 },
	{ 0x4C9429F0, keys570_5k, 0x43 }, // PSPgo 5.70
	{ 0x4C9419F0, keys505_1, 0x43 },
	{ 0x4C9418F0, keys505_0, 0x43 },
	{ 0x457B0BF0, keys505_a, 0x5B },
	{ 0x457B1EF0, keys500_c, 0x5B },
	{ 0x4C941FF0, keys500_2, 0x43 },
	{ 0x4C9417F0, keys500_1, 0x43 },
	{ 0x4C9416F0, keys500_0, 0x43 },
	{ 0x4C9415F0, keys390_1, 0x43 },
	{ 0x4C9414F0, keys390_0, 0x43 },
	{ 0x4C9412F0, keys370_0, 0x43 },
	{ 0x4C9413F0, keys370_1, 0x43 },
	{ 0x457B10F0, keys370_2, 0x5B },
	
	{ 0xD82310F0, keys02G_E, 0x51 }, 
	{ 0xD8231EF0, keys03G_E, 0x51 },
	{ 0xD82328F0, keys05G_E, 0x51 },

	{ 0x4C940DF0, keys360_0, 0x43 },
	{ 0x4C9410F0, keys360_1, 0x43 },
	
	{ 0x4C940BF0, keys330_0, 0x43 }, 
	{ 0x457B0AF0, keys330_1, 0x5B }, 
	{ 0x38020AF0, keys330_2, 0x5A }, 
	{ 0x4C940AF0, keys330_3, 0x43 }, 
	{ 0x4C940CF0, keys330_4, 0x43 }, 

	{ 0xcfef09f0, keys310_0, 0x62 },
	{ 0x457b08f0, keys310_1, 0x5B },
	{ 0x380208F0, keys310_2, 0x5A },
	{ 0xcfef08f0, keys310_3, 0x62 },

	{ 0xCFEF07F0, keys303_0, 0x62 },
	{ 0xCFEF06F0, keys300_0, 0x62 },
	{ 0x457B06F0, keys300_1, 0x5B },
	{ 0x380206F0, keys300_2, 0x5A },
	
	{ 0xCFEF05F0, keys280_0, 0x62 },
	{ 0x457B05F0, keys280_1, 0x5B },
	{ 0x380205F0, keys280_2, 0x5A },
	{ 0x16D59E03, keys260_0, 0x62 },
	{ 0x76202403, keys260_1, 0x5B },
	{ 0x0F037303, keys260_2, 0x5A }
};


static TAG_INFO2 *GetTagInfo2(u32 tagFind)
{
    int iTag;

    for (iTag = 0; iTag < sizeof(g_tagInfo2) / sizeof(TAG_INFO2); iTag++)
	{
        if (g_tagInfo2[iTag].tag == tagFind)
		{
            return &g_tagInfo2[iTag];
		}
	}

	return NULL; // not found
}

static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag)
{
	TAG_INFO2 * pti = GetTagInfo2(tag);

	if (!pti)
	{
		//Kprintf("Unknown tag 0x%08X.\n", tag);
		return -1;
	}	

	int retsize = *(int *)&inbuf[0xB0];
	u8	tmp1[0x150], tmp2[0x90+0x14], tmp3[0x60+0x14], tmp4[0x20];

	memset(tmp1, 0, 0x150);
	memset(tmp2, 0, 0x90+0x14);
	memset(tmp3, 0, 0x60+0x14);
	memset(tmp4, 0, 0x20);

	if (inbuf != outbuf)
		memcpy(outbuf, inbuf, size);

	if (size < 0x160)
	{
		Kprintf("Buffer not big enough.\n");
		return -2;
	}

	if (((u32)outbuf & 0x3F))
	{
		Kprintf("Buffer not aligned to 64 bytes.\n");
		return -3;
	}

	if ((size - 0x150) < retsize)
	{
		Kprintf("No enough data.\n");
		return -4;
	}

	memcpy(tmp1, outbuf, 0x150);

	int i, j;
	u8 *p = tmp2+0x14;

	for (i = 0; i < 9; i++)
	{
		for (j = 0; j < 0x10; j++)
		{
			p[(i << 4) + j] = pti->key[j]; 	
					
		}

		p[(i << 4)] = i;
	}

	if (Scramble((u32 *)tmp2, 0x90, pti->code) < 0)
	{
		Kprintf("Error in Scramble #1.\n");
		return -5;
	}

	memcpy(outbuf, tmp1+0xD0, 0x5C);
	memcpy(outbuf+0x5C, tmp1+0x140, 0x10);
	memcpy(outbuf+0x6C, tmp1+0x12C, 0x14);
	memcpy(outbuf+0x80, tmp1+0x080, 0x30);
	memcpy(outbuf+0xB0, tmp1+0x0C0, 0x10);
	memcpy(outbuf+0xC0, tmp1+0x0B0, 0x10);
	memcpy(outbuf+0xD0, tmp1+0x000, 0x80);

	memcpy(tmp3+0x14, outbuf+0x5C, 0x60);	

	if (Scramble((u32 *)tmp3, 0x60, pti->code) < 0)
	{
		Kprintf("Error in Scramble #2.\n");
		return -6;
	}

	memcpy(outbuf+0x5C, tmp3, 0x60);
	memcpy(tmp3, outbuf+0x6C, 0x14);
	memcpy(outbuf+0x70, outbuf+0x5C, 0x10);
	
	if(pti->type == 3)
	{
		memcpy(tmp4, outbuf+0x3C, 0x20);
		memcpy(outbuf+0x50, tmp4, 0x20);
		memset(outbuf+0x18, 0, 0x38);
	}else
		memset(outbuf+0x18, 0, 0x58);
	
	memcpy(outbuf+0x04, outbuf, 0x04);
	*((u32 *)outbuf) = 0x014C;
	memcpy(outbuf+0x08, tmp2, 0x10);
	
	/* sha-1 */

	if (sceUtilsBufferCopyWithRange(outbuf, 3000000, outbuf, 3000000, 0x0B) != 0)
	{
		Kprintf("Error in sceUtilsBufferCopyWithRange 0xB.\n");
		return -7;
	}

	if (memcmp(outbuf, tmp3, 0x14) != 0)
	{
		Kprintf("SHA-1 is incorrect.\n");
        return -8;
	}
	
	int iXOR;

	for (iXOR = 0; iXOR < 0x40; iXOR++)
	{
		tmp3[iXOR+0x14] = outbuf[iXOR+0x80] ^ tmp2[iXOR+0x10];
	}

	if (Scramble((u32 *)tmp3, 0x40, pti->code) != 0)
	{
		Kprintf("Error in Scramble #2.\n");
		return -9;
	}
	
	for (iXOR = 0x3F; iXOR >= 0; iXOR--)
	{
		outbuf[iXOR+0x40] = tmp3[iXOR] ^ tmp2[iXOR+0x50]; // uns 8
	}

	if (pti->type == 3)
	{
		memcpy(outbuf+0x80, tmp4, 0x20);
		memset(outbuf+0xA0, 0, 0x10);
		*(u32*)&outbuf[0xA4] = 1;
		*(u32*)&outbuf[0xA0] = 1;
	} else
	{
		memset(outbuf+0x80, 0, 0x30);
		*(u32*)&outbuf[0xA0] = 1;
	}

	memcpy(outbuf+0xB0, outbuf+0xC0, 0x10);
	memset(outbuf+0xC0, 0, 0x10);
	memcpy(outbuf+0xD0, outbuf+0xD0, 0x80);

	// The real decryption
	if (sceUtilsBufferCopyWithRange(outbuf, size, outbuf+0x40, size-0x40, 0x1) != 0)
	{
		Kprintf("Error in sceUtilsBufferCopyWithRange 0x1.\n");
		return -1;
	}

	if (retsize < 0x150)
	{
		// Fill with 0
		memset(outbuf+retsize, 0, 0x150-retsize);		
	}

	return retsize;
}

static int _pspDecryptPRX(u32 *arg)
{
	u8 *inbuf = (u8 *)arg[0];
	u8 *outbuf = (u8 *)arg[1];
	u32 size = arg[2];

	int retsize = DecryptPRX1(inbuf, outbuf, size, *(u32 *)&inbuf[0xD0]);

	if (retsize <= 0)
	{
		retsize = DecryptPRX2(inbuf, outbuf, size, *(u32 *)&inbuf[0xD0]);
	}

	return retsize;
}

int pspDecryptPRX(u8 *inbuf, u8 *outbuf, u32 size)
{
	int k1 = pspSdkSetK1(0);
	u32 arg[3];

	arg[0] = (u32)inbuf;
	arg[1] = (u32)outbuf;
	arg[2] = size;
	
	int res = sceKernelExtendKernelStack(0x2000, (void *)_pspDecryptPRX, arg);

	pspSdkSetK1(k1);
	return res;
}

////////// SignCheck //////////

u8 check_keys0[0x10] =
{
	0x71, 0xF6, 0xA8, 0x31, 0x1E, 0xE0, 0xFF, 0x1E,
	0x50, 0xBA, 0x6C, 0xD2, 0x98, 0x2D, 0xD6, 0x2D
};

u8 check_keys1[0x10] =
{
	0xAA, 0x85, 0x4D, 0xB0, 0xFF, 0xCA, 0x47, 0xEB,
	0x38, 0x7F, 0xD7, 0xE4, 0x3D, 0x62, 0xB0, 0x10
};

static int Encrypt(u32 *buf, int size)
{
	buf[0] = 4;
	buf[1] = buf[2] = 0;
	buf[3] = 0x100;
	buf[4] = size;

	/* Note: this encryption returns different data in each psp,
	   But it always returns the same in a specific psp (even if it has two nands) */
	if (sceUtilsBufferCopyWithRange(buf, size+0x14, buf, size+0x14, 5) != 0)
		return -1;

	return 0;
}

int _pspSignCheck(u8 *buf)
{
	u8 enc[0xD0+0x14];
	int iXOR, res;

	memcpy(enc+0x14, buf+0x110, 0x40);
	memcpy(enc+0x14+0x40, buf+0x80, 0x90);
	
	for (iXOR = 0; iXOR < 0xD0; iXOR++)
	{
		enc[0x14+iXOR] ^= check_keys0[iXOR&0xF];
	}

	if ((res = Encrypt((u32 *)enc, 0xD0)) != 0)
	{
		Kprintf("Encrypt failed.\n");
		return -1;
	}

	for (iXOR = 0; iXOR < 0xD0; iXOR++)
	{
		enc[0x14+iXOR] ^= check_keys1[iXOR&0xF];
	}

	memcpy(buf+0x80, enc+0x14, 0xD0);
	
	return 0;
}

int pspSignCheck(u8 *buf)
{
	int k1 = pspSdkSetK1(0);

	int res = sceKernelExtendKernelStack(0x2000, (void *)_pspSignCheck, buf);

	pspSdkSetK1(k1);
	return res;
}

int pspIsSignChecked(u8 *buf)
{
	int k1 = pspSdkSetK1(0);
	int i, res = 0;

	for (i = 0; i < 0x58; i++)
	{
		if (buf[0xD4+i] != 0)
		{
			res = 1;
			break;
		}
	}

	pspSdkSetK1(k1);
	return res;
}

////////// UnsignCheck //////////

static int Decrypt(u32 *buf, int size)
{
	buf[0] = 5;
	buf[1] = buf[2] = 0;
	buf[3] = 0x100;
	buf[4] = size;

	if (sceUtilsBufferCopyWithRange(buf, size+0x14, buf, size+0x14, 8) != 0)
		return -1;
	
	return 0;
}

int _pspUnsignCheck(u8 *buf)
{
	u8 enc[0xD0+0x14];
	int iXOR, res;
	int k1 = pspSdkSetK1(0);

	memcpy(enc+0x14, buf+0x80, 0xD0);

	for (iXOR = 0; iXOR < 0xD0; iXOR++)
	{
		enc[iXOR+0x14] ^= check_keys1[iXOR&0xF]; 
	}

	if ((res = Decrypt((u32 *)enc, 0xD0)) < 0)
	{
		Kprintf("Decrypt failed.\n");
		pspSdkSetK1(k1);
		return res;
	}

	for (iXOR = 0; iXOR < 0xD0; iXOR++)
	{
		enc[iXOR] ^= check_keys0[iXOR&0xF];
	}

	memcpy(buf+0x80, enc+0x40, 0x90);
	memcpy(buf+0x110, enc, 0x40);

	pspSdkSetK1(k1);
	return 0;
}

int pspUnsignCheck(u8 *buf)
{
	int k1 = pspSdkSetK1(0);

	int res = sceKernelExtendKernelStack(0x2000, (void *)_pspUnsignCheck, buf);

	pspSdkSetK1(k1);
	return res;
}
////////// IPL Decryption /////////
int pspDecryptIPL1(const u8* pbIn, u8* pbOut, int cbIn)
{
    int k1 = pspSdkSetK1(0);
	
	// 0x1000 pages
    static u8 g_dataTmp[0x1040] __attribute__((aligned(0x40)));
    int cbOut = 0;
    while (cbIn >= 0x1000)
    {
	    memcpy(g_dataTmp+0x40, pbIn, 0x1000);
        pbIn += 0x1000;
        cbIn -= 0x1000;

        int ret = sceUtilsBufferCopyWithRange(g_dataTmp, 0x1040, g_dataTmp+0x40, 0x500, 1);
	    if (ret != 0)
        {
	        Kprintf("Decrypt IPL 1 failed 0x%08X, WTF!\n", ret);
            break; // stop, save what we can
        }
        memcpy(pbOut, g_dataTmp, 0x1000);
        pbOut += 0x1000;
        cbOut += 0x1000;
    }

	pspSdkSetK1(k1);
    return cbOut;
}

int pspLinearizeIPL2(const u8* pbIn, u8* pbOut, int cbIn)
{
    int k1 = pspSdkSetK1(0);
	
	u32 nextAddr = 0;
    int cbOut = 0;
    while (cbIn > 0)
    {
        u32* pl = (u32*)pbIn;
        u32 addr = pl[0];
        
		if (addr != nextAddr && nextAddr != 0)
		{
            pspSdkSetK1(k1);
			return 0;   // error
		}

        u32 count = pl[1];
        nextAddr = addr + count;
        memcpy(pbOut, pbIn+0x10, count);
        pbOut += count;
        cbOut += count;
        pbIn += 0x1000;
        cbIn -= 0x1000;
    }

	pspSdkSetK1(k1);
    return cbOut;
}

int pspDecryptIPL3(const u8* pbIn, u8* pbOut, int cbIn)
{
    int k1 = pspSdkSetK1(0);
	int ret;
	
	// all together now (pbIn/pbOut must be aligned)
    pbIn += 0x10000;
    cbIn -= 0x10000;
	memcpy(pbOut+0x40, pbIn, cbIn);

	ret = sceUtilsBufferCopyWithRange(pbOut, cbIn+0x40, pbOut+0x40, cbIn, 1);
	if (ret != 0)
    {
		Kprintf("mangle#1 returned $%x\n", ret);
		pspSdkSetK1(k1);
        return 0;
    }

	ret = *(u32*)&pbIn[0x70];  // true size
    
	pspSdkSetK1(k1);
	return ret;
}

////////// Decompression //////////

int pspIsCompressed(u8 *buf)
{
	int k1 = pspSdkSetK1(0);
	int res = 0;

	if (buf[0] == 0x1F && buf[1] == 0x8B)
		res = 1;
	else if (memcmp(buf, "2RLZ", 4) == 0)
		res = 1;

	pspSdkSetK1(k1);
	return res;
}


int decompress_kle(void *outbuf, u32 outcapacity, void *inbuf, void *unk)
{
	int (* decompress)(void *, u32, void *, void *);
	
	u32 *mod = (u32 *)sceKernelFindModuleByName("sceLoadExec");
	u32 text_addr = *(mod+27);
	decompress = (void *)(text_addr+0);

	return decompress(outbuf, outcapacity, inbuf, unk);
}

static int _pspDecompress(u32 *arg)
{
	int retsize;
	u8 *inbuf = (u8 *)arg[0];
	u8 *outbuf = (u8 *)arg[1];
	u32 outcapacity = arg[2];
	
	if (inbuf[0] == 0x1F && inbuf[1] == 0x8B) 
	{
		retsize = sceKernelGzipDecompress(outbuf, outcapacity, inbuf, NULL);
	}
	else if (memcmp(inbuf, "2RLZ", 4) == 0) 
	{
		int (*lzrc)(void *outbuf, u32 outcapacity, void *inbuf, void *unk) = NULL;
		
		if (sceKernelDevkitVersion() >= 0x03080000)
		{
			
			u32 *mod = (u32 *)sceKernelFindModuleByName("sceNp9660_driver");
			if (!mod)
				return -1;

			u32 *code = (u32 *)mod[27];

			int i;
			
			for (i = 0; i < 0x8000; i++)
			{
				if (code[i] == 0x27bdf4f0 && code[i+20] == 0x34018080)
				{
					lzrc = (void *)&code[i];
					break;
				} 
			}

			if (i == 0x8000)
				return -2;
			//lzrc = lzrc_;
		}
		else
		{
			lzrc = (void *)sctrlHENFindFunction("sceSystemMemoryManager", "UtilsForKernel", 0x7DD07271);

		}
		
		retsize = lzrc(outbuf, outcapacity, inbuf+4, NULL);
	}
	else if (memcmp(inbuf, "KL4E", 4) == 0)
	{
		retsize = UtilsForKernel_6C6887EE(outbuf, outcapacity, inbuf+4, NULL);
	}
	else if (memcmp(inbuf, "KL3E", 4) == 0) 
	{
		retsize = decompress_kle(outbuf, outcapacity, inbuf+4, NULL);
	}
	else
	{
		retsize = -1;
	}

	return retsize;
}

int pspDecompress(const u8 *inbuf, u8 *outbuf, u32 outcapacity)
{
	int k1 = pspSdkSetK1(0);
	u32 arg[3];

	arg[0] = (u32)inbuf;
	arg[1] = (u32)outbuf;
	arg[2] = outcapacity;
	
	int res = sceKernelExtendKernelStack(0x2000, (void *)_pspDecompress, arg);
	
	pspSdkSetK1(k1);
	return res;
}

////////// 3.70+ Table Insanity //////////

u8 key_C[56] = 
{
	0x07, 0x0F, 0x17, 0x1F, 0x27, 0x2F, 0x37, 0x3F, 0x06, 0x0E, 0x16, 0x1E, 0x26, 0x2E, 0x36, 0x3E, 
	0x05, 0x0D, 0x15, 0x1D, 0x25, 0x2D, 0x35, 0x3D, 0x04, 0x0C, 0x14, 0x1C, 0x01, 0x09, 0x11, 0x19, 
	0x21, 0x29, 0x31, 0x39, 0x02, 0x0A, 0x12, 0x1A, 0x22, 0x2A, 0x32, 0x3A, 0x03, 0x0B, 0x13, 0x1B, 
	0x23, 0x2B, 0x33, 0x3B, 0x24, 0x2C, 0x34, 0x3C
};

u8 key_Z[48] = 
{
	0x32, 0x2F, 0x35, 0x28, 0x3F, 0x3B, 0x3D, 0x24, 0x31, 0x3A, 0x2B, 0x36, 0x29, 0x2D, 0x34, 0x3C, 
	0x26, 0x38, 0x30, 0x39, 0x25, 0x2C, 0x33, 0x3E, 0x17, 0x0C, 0x21, 0x1B, 0x11, 0x09, 0x22, 0x18, 
	0x0D, 0x13, 0x1F, 0x10, 0x14, 0x0F, 0x19, 0x08, 0x1E, 0x0B, 0x12, 0x16, 0x0E, 0x1C, 0x23, 0x20
};

u8 key_M[512] = 
{
	0x0D, 0x01, 0x02, 0x0F, 0x08, 0x0D, 0x04, 0x08, 0x06, 0x0A, 0x0F, 0x03, 0x0B, 0x07, 0x01, 0x04, 
	0x0A, 0x0C, 0x09, 0x05, 0x03, 0x06, 0x0E, 0x0B, 0x05, 0x00, 0x00, 0x0E, 0x0C, 0x09, 0x07, 0x02, 
	0x07, 0x02, 0x0B, 0x01, 0x04, 0x0E, 0x01, 0x07, 0x09, 0x04, 0x0C, 0x0A, 0x0E, 0x08, 0x02, 0x0D, 
	0x00, 0x0F, 0x06, 0x0C, 0x0A, 0x09, 0x0D, 0x00, 0x0F, 0x03, 0x03, 0x05, 0x05, 0x06, 0x08, 0x0B, 
	0x04, 0x0D, 0x0B, 0x00, 0x02, 0x0B, 0x0E, 0x07, 0x0F, 0x04, 0x00, 0x09, 0x08, 0x01, 0x0D, 0x0A, 
	0x03, 0x0E, 0x0C, 0x03, 0x09, 0x05, 0x07, 0x0C, 0x05, 0x02, 0x0A, 0x0F, 0x06, 0x08, 0x01, 0x06, 
	0x01, 0x06, 0x04, 0x0B, 0x0B, 0x0D, 0x0D, 0x08, 0x0C, 0x01, 0x03, 0x04, 0x07, 0x0A, 0x0E, 0x07, 
	0x0A, 0x09, 0x0F, 0x05, 0x06, 0x00, 0x08, 0x0F, 0x00, 0x0E, 0x05, 0x02, 0x09, 0x03, 0x02, 0x0C, 
	0x0C, 0x0A, 0x01, 0x0F, 0x0A, 0x04, 0x0F, 0x02, 0x09, 0x07, 0x02, 0x0C, 0x06, 0x09, 0x08, 0x05, 
	0x00, 0x06, 0x0D, 0x01, 0x03, 0x0D, 0x04, 0x0E, 0x0E, 0x00, 0x07, 0x0B, 0x05, 0x03, 0x0B, 0x08, 
	0x09, 0x04, 0x0E, 0x03, 0x0F, 0x02, 0x05, 0x0C, 0x02, 0x09, 0x08, 0x05, 0x0C, 0x0F, 0x03, 0x0A, 
	0x07, 0x0B, 0x00, 0x0E, 0x04, 0x01, 0x0A, 0x07, 0x01, 0x06, 0x0D, 0x00, 0x0B, 0x08, 0x06, 0x0D, 
	0x02, 0x0E, 0x0C, 0x0B, 0x04, 0x02, 0x01, 0x0C, 0x07, 0x04, 0x0A, 0x07, 0x0B, 0x0D, 0x06, 0x01, 
	0x08, 0x05, 0x05, 0x00, 0x03, 0x0F, 0x0F, 0x0A, 0x0D, 0x03, 0x00, 0x09, 0x0E, 0x08, 0x09, 0x06, 
	0x04, 0x0B, 0x02, 0x08, 0x01, 0x0C, 0x0B, 0x07, 0x0A, 0x01, 0x0D, 0x0E, 0x07, 0x02, 0x08, 0x0D, 
	0x0F, 0x06, 0x09, 0x0F, 0x0C, 0x00, 0x05, 0x09, 0x06, 0x0A, 0x03, 0x04, 0x00, 0x05, 0x0E, 0x03, 
	0x07, 0x0D, 0x0D, 0x08, 0x0E, 0x0B, 0x03, 0x05, 0x00, 0x06, 0x06, 0x0F, 0x09, 0x00, 0x0A, 0x03, 
	0x01, 0x04, 0x02, 0x07, 0x08, 0x02, 0x05, 0x0C, 0x0B, 0x01, 0x0C, 0x0A, 0x04, 0x0E, 0x0F, 0x09, 
	0x0A, 0x03, 0x06, 0x0F, 0x09, 0x00, 0x00, 0x06, 0x0C, 0x0A, 0x0B, 0x01, 0x07, 0x0D, 0x0D, 0x08, 
	0x0F, 0x09, 0x01, 0x04, 0x03, 0x05, 0x0E, 0x0B, 0x05, 0x0C, 0x02, 0x07, 0x08, 0x02, 0x04, 0x0E, 
	0x0A, 0x0D, 0x00, 0x07, 0x09, 0x00, 0x0E, 0x09, 0x06, 0x03, 0x03, 0x04, 0x0F, 0x06, 0x05, 0x0A, 
	0x01, 0x02, 0x0D, 0x08, 0x0C, 0x05, 0x07, 0x0E, 0x0B, 0x0C, 0x04, 0x0B, 0x02, 0x0F, 0x08, 0x01, 
	0x0D, 0x01, 0x06, 0x0A, 0x04, 0x0D, 0x09, 0x00, 0x08, 0x06, 0x0F, 0x09, 0x03, 0x08, 0x00, 0x07, 
	0x0B, 0x04, 0x01, 0x0F, 0x02, 0x0E, 0x0C, 0x03, 0x05, 0x0B, 0x0A, 0x05, 0x0E, 0x02, 0x07, 0x0C, 
	0x0F, 0x03, 0x01, 0x0D, 0x08, 0x04, 0x0E, 0x07, 0x06, 0x0F, 0x0B, 0x02, 0x03, 0x08, 0x04, 0x0E, 
	0x09, 0x0C, 0x07, 0x00, 0x02, 0x01, 0x0D, 0x0A, 0x0C, 0x06, 0x00, 0x09, 0x05, 0x0B, 0x0A, 0x05, 
	0x00, 0x0D, 0x0E, 0x08, 0x07, 0x0A, 0x0B, 0x01, 0x0A, 0x03, 0x04, 0x0F, 0x0D, 0x04, 0x01, 0x02, 
	0x05, 0x0B, 0x08, 0x06, 0x0C, 0x07, 0x06, 0x0C, 0x09, 0x00, 0x03, 0x05, 0x02, 0x0E, 0x0F, 0x09, 
	0x0E, 0x00, 0x04, 0x0F, 0x0D, 0x07, 0x01, 0x04, 0x02, 0x0E, 0x0F, 0x02, 0x0B, 0x0D, 0x08, 0x01, 
	0x03, 0x0A, 0x0A, 0x06, 0x06, 0x0C, 0x0C, 0x0B, 0x05, 0x09, 0x09, 0x05, 0x00, 0x03, 0x07, 0x08, 
	0x04, 0x0F, 0x01, 0x0C, 0x0E, 0x08, 0x08, 0x02, 0x0D, 0x04, 0x06, 0x09, 0x02, 0x01, 0x0B, 0x07, 
	0x0F, 0x05, 0x0C, 0x0B, 0x09, 0x03, 0x07, 0x0E, 0x03, 0x0A, 0x0A, 0x00, 0x05, 0x06, 0x00, 0x0D
};

u8 key_S[8] = 
{
	0x9E, 0xA4, 0x33, 0x81, 0x86, 0x0C, 0x52, 0x85
};

u8 key_S2[8] = 
{
	0xB2, 0xFE, 0xD9, 0x79, 0x8A, 0x02, 0xB1, 0x87
};

u8 key_S3[8] = 
{
	0x81, 0x08, 0xC1, 0xF2, 0x35, 0x98, 0x69, 0xB0 
};

u8 key_S4[8] =
{
	0x6D, 0x52, 0x1B, 0xA3, 0xC2, 0x36, 0xF9, 0x2B
};

u8 key_S5[8] =
{
	0xDB, 0x4E, 0x79, 0x41, 0xF5, 0x97, 0x30, 0xAD
};

u8 key_S6[8] =
{
	0xA6, 0x83, 0x0C, 0x2F, 0x63, 0x0B, 0x96, 0x29
};

u8 table_40[128] = 
{
	0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 
	0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 
	0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 
	0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 
	0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 
	0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 
	0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00
};


typedef struct
{
	u32 mklow;
	u32 mkhigh;
	u8 *key_S;
} TABLE_KEYS;

TABLE_KEYS table_keys[] =
{
	{ 0xb730e5c7, 0x95620b49, key_S  },
	{ 0x45c9dc95, 0x5a7b3d9d, key_S2 },
	{ 0x6F20585A, 0x4CCE495B, key_S3 },
	{ 0x620BF15A, 0x73F45262, key_S4 },
	{ 0xFD9D4498, 0xA664C8F8, key_S5 },
    { 0x3D6426E7, 0xD7BD7481, key_S6 }
};

static void GenerateSeed(void *out, int unused, u32 c1, u32 c2)
{
	u32 i, j, val1, val2, bit_insert, r1, r2, shr, base, wpar1, wpar2;
	u32 *out32 = (u32 *)out;
	
	bit_insert = 0x80000000;
	r1 = r2 = base = 0;

	for (i = 0; i < 0x38; i++)
	{
		val1 = key_C[i];
		
		if (val1 & 0x20)
		{
			val2 = (1 << val1);
			val1 = 0;			
		}
		else
		{
			val1 = (1 << val1);
			val2 = 0;
		}		

		val1 = (c1 & val1);
		val2 = (c2 & val2);
		
		if ((val1 | val2))
		{
			val1 = base;
			val2 = bit_insert;
		}
		else
		{
			val1 = 0;
			val2 = 0;
		}

		r1 = (r1 | val1);
		r2 = (r2 | val2);
		base = (base >> 1);
		base = (base & 0x7FFFFFFF) | ((bit_insert & 1) << 31); 
		bit_insert = (bit_insert >> 1);
	}

	wpar1 = (r2 >> 4);
	wpar2 = (r1 >> 8) & 0x00FFFFFF; 
	wpar2 = (wpar2  & 0xF0FFFFFF) | ((r2 & 0xF) << 24); 

	for (i = 0x10; i != 0; i--)
	{	
		r1 = 0x7efc;
		val1 = (wpar1 << 4);
		r1 = (r1 >> i);
		val2 = (wpar2 << 4);
		r1 = (r1 & 1);
		shr = (r1 ^ 0x1F);
		r1++;
		val1 = (val1 >> shr);
		val2 = (val2 >> shr);
		wpar1 = (wpar1 << r1);
		wpar2 = (wpar2 << r1);
		wpar1 = (wpar1 | val1);
		wpar2 = (wpar2 | val2);
		wpar1 = (wpar1 & 0x0FFFFFFF);
		wpar2 = (wpar2 & 0x0FFFFFFF);
		c2 = (wpar2 >> 24);
		c2 = (c2&0xF) | ((wpar1 & 0x0FFFFFFF) << 4);
		c1 = (wpar2 << 8);

		base = r1 = r2 = 0;
		bit_insert = 0x80000000;
		
		for (j = 0; j < 0x30; j++)
		{
			val1 = key_Z[j];
			
			if (val1 & 0x20)
			{
				val2 = (1 << val1);
				val1 = 0;				
			}
			else
			{
				val1 = (1 << val1);
				val2 = 0;
			}

			val1 = (c1 & val1);
			val2 = (c2 & val2);
			
			if ((val1 |val2))
			{
				val1 = base;
				val2 = bit_insert;
			}
			else
			{
				val1 = 0;
				val2 = 0;
			}

			r1 = (r1 | val1);
			r2 = (r2 | val2);
			base = (base >> 1);
			base = (base & 0x7FFFFFFF) | ((bit_insert & 1) << 31); 
			bit_insert = (bit_insert >> 1);
		}

		out32[0] = r1;
		out32[1] = r2;
		out32 += 2;
	}
}

static void Sce_Insanity_1(u32 x1, u32 x2, u32 *r1, u32 *r2)
{
	u32 temp;
	
	temp = ((x2 >> 4) ^ x1) & 0x0F0F0F0F;
	x2 = x2 ^ (temp << 4);
	x1 = x1 ^ temp;
	temp = ((x2 >> 16) ^ x1) & 0xFFFF;
	x1 = x1 ^ temp;
	x2 = x2 ^ (temp << 16);
	temp = ((x1 >> 2) ^ x2) & 0x33333333;
	x1 = x1 ^ (temp << 2);
	x2 = x2 ^ temp;
	temp = ((x1 >> 8) ^ x2) & 0x00FF00FF;
	x2 = (x2 ^ temp);
	x1 = x1 ^ (temp << 8);
	temp = ((x2 >> 1) ^ x1) & 0x55555555;
	*r2 = x2 ^ (temp << 1);
	*r1 = x1 ^ temp;
}

static void Sce_Insanity_2(u32 x1, u32 x2, u32 *r1, u32 *r2)
{
	u32 h1, h2, h3, h4;

	h1 = (x2 & 1);
	h2 = (x2 >> 27) & 0x1F;
	h3 = (x2 >> 23) & 0x3F;
	h4 = (x2 >> 19) & 0x3F;
	*r2 = (x2 >> 15) & 0x3F;
	*r2 = (*r2 & 0xFF7FFFFF) | ((h1 & 1) << 23);
	*r2 = (*r2 & 0xFF83FFFF) | ((h2 & 0x1F) << 18);
	*r2 = (*r2 & 0xFFFC0FFF) | ((h3 & 0x3F) << 12);
	*r2 = (*r2 & 0xFFFFF03F) | ((h4 & 0x3F) << 6);
	h1 = (x2 >> 11) & 0x3F;
	h2 = (x2 >> 7) & 0x3F;
	h3 = (x2 >> 3) & 0x3F;
	h4 = (x2 & 0x1F);
	*r1 = (x2 >> 31) & 1;
	*r1 = (*r1 & 0xFF03FFFF) | ((h1 & 0x3F) << 18);
	*r1 = (*r1 & 0xFFFC0FFF) | ((h2 & 0x3F) << 12);
	*r1 = (*r1 & 0xFFFFF03F) | ((h3 & 0x3F) << 6);
	*r1 = (*r1 & 0xFFFFFFC1) | ((h4 & 0x1F) << 1);
	
	*r2 = ((*r2 << 8) | ((*r1 >> 16) & 0xFF));
	*r1 = (*r1 << 16);
}

static void Sce_Insanity_3(u32 x1, u32 x2, u32 *r1, u32 *r2)
{
	int i;
	u32 shifter = 0, val;
	u8 *p = key_M;

	*r2 = 0;

	for (i = 0; i < 8; i++)
	{
		val = p[x1&0x3F];
		p += 0x40;
		x1 = (x1 >> 6);
		x1 = (x1 & 0x03FFFFFF) | ((x2 & 0x3F) << 26);
		x2 = (x2 >> 6);
		*r2 |= (val << shifter);
		shifter += 4;
	}

	*r1 = 0;
}

static void Sce_Insanity_4(u32 x1, u32 x2, u32 *r1, u32 *r2)
{
	int i;
	u32 *table = (u32 *)table_40;

	*r1 = 0;
	*r2 = 0;

	for (i = 0; i < 0x20; i++)
	{
		if (x2 & 1)
		{
			*r2 |= table[i];
		}		

		x2 = (x2 >> 1);		
	}
}

static void Sce_Insanity_5(u32 x1, u32 x2, u32 *r1, u32 *r2)
{
	u32 temp;

	temp = ((x2 >> 1) ^ x1) & 0x55555555;
	x1 = x1 ^ temp;
	x2 = x2 ^ (temp << 1);
	temp = ((x1 >> 8) ^ x2) & 0x00FF00FF;
	x1 = x1 ^ (temp << 8);
	x2 = x2 ^ temp;
	temp = ((x1 >> 2) ^ x2) & 0x33333333;
	x2 = x2 ^ temp;
	x1 = x1 ^ (temp << 2);
	temp = ((x2 >> 16) ^ x1) & 0xFFFF;
	x2 = x2 ^ (temp << 16);
	x1 = x1 ^ temp;
	temp = ((x2 >> 4) ^ x1) & 0x0F0F0F0F;
	*r1 = x1 ^ temp;
	*r2 = x2 ^ (temp << 4);
}

static void Sce_Paranoia(u8 *buf, u32 unused, u32 *p1, u32 *p2)
{
	u32 x1 = *p1;
	u32 x2 = *p2;
	u32 r1, r2, rot1, rot2, rot3, rot4, ro1, ro2, base;
	int i;
	u8 *p = buf+0x78;

	Sce_Insanity_1(x1, x2, &r1, &r2); 

	rot1 = 0;
	rot2 = 0;
	rot3 = r1;
	rot4 = r2;

	for (i = 0; i < 0x10; i++)
	{
		Sce_Insanity_2(rot1, rot3, &r1, &r2);

		ro1 = r1;
		ro2 = r2;
		r1 = *(u32 *)&p[0];
		r2 = *(u32 *)&p[4];
		p -= 8;
		base = (ro2 ^ r2);
		x1 = (base << 16);
		
		Sce_Insanity_3(((ro1 ^ r1) >> 16) | x1, base >> 16, &r1, &r2);
		Sce_Insanity_4(r1, r2, &r1, &r2);

		x1 = (r1 ^ rot2);
		x2 = (r2 ^ rot4);
		rot2 = rot1;
		rot4 = rot3;
		rot1 = x1;
		rot3 = x2;
	}

	Sce_Insanity_5(x1 | rot4, x2, p1, p2);
}

static void DecryptT(u8 *buf, int size, int mode)
{
	u8 m1[0x400];
	u8 m2[8];
	int i, j;

	memset(m1, 0, sizeof(m1));
	
	GenerateSeed(m1, 0x33333333, table_keys[mode].mklow, table_keys[mode].mkhigh);
	
	memcpy(m1+0x80, table_keys[mode].key_S, 8);

	for (i = 0; i < size; i++)
	{
		for (j = 0; j < 8; j++)
		{
			m2[7-j] = buf[j];
		}

		Sce_Paranoia(m1, 0x33333333, (u32 *)&m2[0], (u32 *)&m2[4]);

		for (j = 0; j < 8; j++)
		{
			m1[0x90+j] = m2[7-j] ^ m1[0x80+j];
			m1[0x80+j] = buf[j];
		}

		*(u32 *)&buf[0] = *(u32 *)&m1[0x90];
		*(u32 *)&buf[4] = *(u32 *)&m1[0x94];

		buf += 8;
	}	
}

static int pspDecryptTable_(u32 *arg)
{
	u8 *buf1 = (u8 *)arg[0];
	u8 *buf2 = (u8 *)arg[1];
	int size = (int)arg[2];
	int mode = (int)arg[3];
	int retsize;

	DecryptT(buf1, size >> 3, mode);

	if (buf1 != buf2) memcpy(buf2, buf1, size);
	
/*
	if ((*(u32 *)&buf2[0xD0]) == 0xD82310F0)
	{
		retsize = DecryptPRX2(buf2, buf1, size, 0xD82310F0);		
	}
	else if ((*(u32 *)&buf2[0xD0]) == 0xD8231EF0)
	{
		retsize = DecryptPRX2(buf2, buf1, size, 0xD8231EF0);		
	}
	else
*/
	retsize = pspDecryptPRX(buf2, buf1, size);
	if (retsize < 0)
	{	
		int res = sceMesgd_driver_102DC8AF(buf1, size, &retsize);
		if (res < 0)
		{
			retsize = -1;				
		}
	}

	return retsize;
}

int pspDecryptTable(u8 *buf1, u8 *buf2, int size, int mode)
{
	u32 arg[4];
	int retsize;
	int k1 = pspSdkSetK1(0);

	arg[0] = (u32)buf1;
	arg[1] = (u32)buf2;
	arg[2] = (u32)size;
	arg[3] = (u32)mode;

	retsize = sceKernelExtendKernelStack(0x2000, (void *)pspDecryptTable_, arg);

	pspSdkSetK1(k1);
	return retsize;
}

int module_start(SceSize args, void *argp)
{
	return 0;
}

int module_stop(void)
{
	return 0;
}
